package ControllerApplication;

import jAudioFeatureExtractor.jAudioTools.FFT;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import ModelApplication.ComplexArray;
import ModelApplication.Filter;
import ModelApplication.Sound;
import ProcessingAudioWindows.AudioProcessingWindowLocal;
import ProcessingAudioWindows.W1AudioProcessingWindow;
import UtilsApplication.ChartTypes;
import UtilsApplication.FilterSpectrum;
import UtilsApplication.HandleAudioUtil;

public class W1AudioProcessing extends AudioProcessing {

    public W1AudioProcessing( String methodName, AudioProcessingWindowLocal imgWindow ) {
        super( methodName, imgWindow );
    }
    @Override
    public List<Sound> processingList(Sound sound) {

        W1AudioProcessingWindow imgW = (W1AudioProcessingWindow) imgWindow;
        List<Sound> sounds = new ArrayList<Sound>();
        double [] origAudio = sound.getAudio();

        double [] audio = Arrays.copyOfRange( origAudio, 0, origAudio.length );

        List<Filter> filters = null;
        
        if(!imgW.isGetFilters()) {
            filters = new ArrayList<Filter>();
            int filterCount = imgW.getFilterCounts(); 
            int beginFreq = 20;
            int endFreq = 20000;
            double q = Math.pow( endFreq / beginFreq, (1.0/(double)((double)filterCount)));
            for(int i = 2; i <= filterCount + 1; i++){
                filters.add( new Filter((int)(beginFreq * Math.pow( q, i - 2 )),
                        (int) (beginFreq * Math.pow( q, i - 1 )) ) );
            }
        } else {
            filters = imgW.getFilters();
        }
        
      


        List<AudioOutput> outputs = new ArrayList<AudioOutput>();

        for(int i = 0; i <= filters.size(); i++) {
            outputs.add( new AudioOutput(audio.length) );
        }

        //ladowanie szumu
        Sound noise = HandleAudioUtil.loadSound( System.getProperty("user.dir") + "/soundExamples/carrier/" + imgW.getCarrierName() );

        int len = imgW.getWindowWidth();
        double overlap = 1;
        if(imgW.getWindowOverlap() == 1) {
            overlap = 0.5;
        } else if(imgW.getWindowOverlap() == 2) {
            overlap = 0.75;
        } 
        
        int noiseLen = 0;

        long start = System.currentTimeMillis();
        for(int i = 0; i < audio.length - 1; i += (len * overlap)) {

            if(len + i > audio.length) {
                len = audio.length - i - 1;
                if(len <= 2) {
                    break;
                }
            }

            double [] audioArray = Arrays.copyOfRange( audio, i, len + i );
            if(len + noiseLen > noise.getAudio().length) {
                noiseLen = 0;
            }
            double [] noiseArray = Arrays.copyOfRange( noise.getAudio(), noiseLen, len + noiseLen );

            outputs = vocoderWindow(filters, outputs, audioArray, noiseArray, len, i);

            noiseLen += len;
        }

//        for ( int i = 0; i < outputs.size() - 1; i++ ) {
//            Sound s = new Sound(outputs.get( i ).filterAudio, sound.getWavFile(), null);
//            s.setEnvelope( outputs.get( i ).envelope );
//            s.setChartType( ChartTypes.SOUND_ENVELOPE );
//            sounds.add( s );
//        }
        
        long time = System.currentTimeMillis() - start;
        Sound synthSound = new Sound(HandleAudioUtil.normalizeSingal( outputs.get( outputs.size() - 1 ).filterAudio ), sound.getWavFile(), null);
        synthSound.setExecutionTime( time / 1000.0 );
        sounds.add(synthSound );

        return sounds;
    }


    private List<AudioOutput> vocoderWindow(List<Filter> filters, List<AudioOutput> outputs, double [] audio, double [] noise, int len, int pos) {
        // transformata
        FFT fftAudio = null;
        try {
            fftAudio = new FFT(audio, null, false, false );
        } catch ( Exception e1 ) {
            e1.printStackTrace();
        }

        ComplexArray spectrum = new ComplexArray(fftAudio.getRealValues(), fftAudio.getImaginaryValues());

        FFT fftNoise = null;
        try {
            fftNoise = new FFT(noise, null, false, true );
        } catch ( Exception e1 ) {
            e1.printStackTrace();
        }

        ComplexArray spectrumNoise = new ComplexArray(fftNoise.getRealValues(), fftNoise.getImaginaryValues());

        int inx = 0;
        for(Filter f : filters) {

            ComplexArray sp = FilterSpectrum.filter( f, spectrum);
            ComplexArray szsp = FilterSpectrum.filter( f, spectrumNoise);


            FFT fft = null;
            try {
                fft = new FFT( sp.getReal(),sp.getImg(), true, false );
            } catch ( Exception e1 ) {
                e1.printStackTrace();
            }
            double [] filterAudio = fft.getRealValues();
            double [] envelope = envelope( filterAudio );

            FFT fft2 = null;
            try {
                fft2 = new FFT( szsp.getReal(),szsp.getImg(), true, true );
            } catch ( Exception e ) {
                e.printStackTrace();
            }

            double [] szumarray2 = fft2.getRealValues();

            for ( int i = 0; i < len; i++ ) {
                outputs.get(inx).filterAudio[i + pos] = filterAudio[i];
                outputs.get(inx).envelope[i + pos] = envelope[i];

                outputs.get(outputs.size() - 1).filterAudio[i + pos] += szumarray2[i] * envelope[i];
            }

            inx++;
        }

        return outputs;
    }

    private double [] envelope(double [] array){
        double max = -1;
        for(int i = 0; i < array.length; i++) {
            if(max < array[i]) {
                max = array[i];
            }
        }
        double step = max*0.0004;
        double [] obwiednia = new double[array.length];
        double cur=0;
        for (int i = 0; i < array.length; i++) {
            cur = cur - step;
            if(array[i] > cur) {
                cur = array[i];
            }
            obwiednia[i] = cur;
        }
        cur=0;
        for (int i = array.length - 1; i >= 0; i--) {
            cur = cur - step;
            if (array[i]>cur) {
                cur = array[i];
            }
            if(cur > obwiednia[i]) {
                obwiednia[i] = cur;
            }
        }
        return obwiednia;
    }

    class AudioOutput {
        double [] filterAudio;
        double [] envelope;

        public AudioOutput(int length) {
            filterAudio = new double [length];
            envelope = new double[length];
        }
    }

}
